/***********************************************************************
    created:    10/7/2013
    author:     Timotei Dolean <timotei21@gmail.com>

    purpose:    Generates input events from injected input
*************************************************************************/
/***************************************************************************
 *   Copyright (C) 2004 - 2013 Paul D Turner & The CEGUI Development Team
 *
 *   Permission is hereby granted, free of charge, to any person obtaining
 *   a copy of this software and associated documentation files (the
 *   "Software"), to deal in the Software without restriction, including
 *   without limitation the rights to use, copy, modify, merge, publish,
 *   distribute, sublicense, and/or sell copies of the Software, and to
 *   permit persons to whom the Software is furnished to do so, subject to
 *   the following conditions:
 *
 *   The above copyright notice and this permission notice shall be
 *   included in all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *   OTHER DEALINGS IN THE SOFTWARE.
 ***************************************************************************/
#ifndef _CEGUIInputAggregator_h_
#define _CEGUIInputAggregator_h_

#include "CEGUI/Base.h"
#include "CEGUI/Event.h"
#include "CEGUI/EventSet.h"
#include "CEGUI/InjectedInputReceiver.h"
#include "CEGUI/InputEventReceiver.h"
#include "CEGUI/SemanticInputEvent.h"

#include <cstdint>

#if defined (_MSC_VER)
#   pragma warning(push)
#   pragma warning(disable : 4251)
#endif

// Start of CEGUI namespace section
namespace CEGUI
{
struct MouseClickTracker;

//! EventArgs class passed to subscribers for (most) InputAggregator events.
class CEGUIEXPORT InputAggregatorEventArgs : public EventArgs
{
public:
    InputAggregatorEventArgs(InputAggregator* aggregator):
        d_aggregator(aggregator)
    {}

    //! pointer to the InputAggregator that triggered the event.
    InputAggregator* d_aggregator;
};

/*!
\brief
    Aggregates the input from multiple input devices and processes it to generate
    input events which are then fed to the (optional) \ref InputEventReceiver.
*/
class CEGUIEXPORT InputAggregator : public InjectedInputReceiver,
                                    public EventSet
{
public:
    static const float DefaultMouseButtonClickTimeout;
    static const float DefaultMouseButtonMultiClickTimeout;
    static const Sizef DefaultMouseButtonMultiClickTolerance;

    /** Name of Event fired when the mouse click timeout is changed.
     * Handlers are passed a const reference to a GUIContextEventArgs struct.
     */
    static const String EventMouseButtonClickTimeoutChanged;
    /** Name of Event fired when the mouse multi-click timeout is changed.
     * Handlers are passed a const reference to a GUIContextEventArgs struct.
     */
    static const String EventMouseButtonMultiClickTimeoutChanged;
    /** Name of Event fired when the mouse multi-click movement tolerance area
     * size is changed.
     * Handlers are passed a const reference to a GUIContextEventArgs struct.
     */
    static const String EventMouseButtonMultiClickToleranceChanged;
    /** Name of Event fired when the mouse movement scaling factor is changed.
     * Handlers are passed a const reference to a GUIContextEventArgs struct.
     */
    static const String EventMouseMoveScalingFactorChanged;

    InputAggregator(InputEventReceiver* input_receiver);
    virtual ~InputAggregator();

    /*!
    \brief
        Initialises this InputAggregator with some default simple-key mappings
    \param handle_on_keyup
        When set to true semantic actions will be registered on key up
        otherwise semantic actions are handled on key down. If false
        it is recommended to call setModifierKeys before any injectKeyDown
        calls to make sure that modifiers are properly set.
    */
    virtual void initialise(bool handle_on_keyup = true);

    /*!
    \brief
        When true semantic actions will be registered on key up
        otherwise semantic actions are handled on key down.
    */
    bool getHandleOnKeyUp() const     { return d_handleInKeyUp; }

    /*!
    \brief
        Set whether automatic mouse button click and multi-click (i.e.
        double-click and treble-click) event generation will occur.

    \param enable
        - true to have mouse button click and multi-click events automatically
        generated by the system from the basic button up and down event
        injections.
        - false if no automatic generation of events should occur.  In this
        instance the user may wish to use the additional event injectors to
        manually inform the system of such events.
    */
    void setMouseClickEventGenerationEnabled(const bool enable);

    /*!
    \brief
        Return whether automatic mouse button click and multi-click (i.e.
        double-click and treble-click) event generation is enabled.

    \return
        - true if mouse button click and multi-click events will be
        automatically generated by the system from the basic button up and down
        event injections.
        - false if no automatic generation of events will occur.  In this
        instance the user may wish to use the additional event injectors to
        manually inform the system of such events.
    */
    bool isMouseClickEventGenerationEnabled() const;

    void setMouseButtonClickTimeout(float seconds);
    float getMouseButtonClickTimeout() const;

    void setMouseButtonMultiClickTimeout(float seconds);
    float getMouseButtonMultiClickTimeout() const;

    /*!
    \brief
        Sets the mouse multi-click tolerance size

    \param sz
        The size of the tolerance in percent of the display's size
    */
    void setMouseButtonMultiClickTolerance(const Sizef& sz);

    /*!
    \brief
        Returns the mouse multi-click tolerance size

    \return
        A size structure with the zone's width and height in percent of the
        display's size
    */
    const Sizef& getMouseButtonMultiClickTolerance() const;

    void setMouseMoveScalingFactor(float factor);
    float getMouseMoveScalingFactor() const;

    /*!
    \brief
        Returns a semantic action matching the scan_code
    */
    SemanticValue getSemanticAction(Key::Scan scan_code, bool shift_down, bool alt_down,
        bool ctrl_down) const;

    /*!
    \brief
        Gets semantic action for scan_code and sends the event
    \return
        True if the semantic action was handled
    */
    bool handleScanCode(Key::Scan scan_code, bool shift_down, bool alt_down,
        bool ctrl_down);
    
    /*!
    \brief
        Sets the status of modifier keys to the specified values.

        Call this before injectKeyDown if InputAggregator is set to handle
        actions on keydown.
     */
    void setModifierKeys(bool shift_down, bool alt_down, bool ctrl_down);
    
    /************************************************************************/
    /* InjectedInputReceiver interface implementation                       */
    /************************************************************************/
    bool injectMouseMove(float delta_x, float delta_y) override;
    bool injectMouseLeaves() override;

    bool injectMouseButtonDown(MouseButton button) override;
    bool injectMouseButtonUp(MouseButton button) override;

    /*!
    \return
        True if set to handle keys on key down and the input was consumed.
        When not to set handle actions on key down will always return true.
    */
    bool injectKeyDown(Key::Scan scan_code) override;
    /*!
    \return
        True if set to handle keys on key up and the input was consumed.
        When not to set handle actions on key up will always return true.
    */
    bool injectKeyUp(Key::Scan scan_code) override;

    bool injectChar(char32_t code_point) override;
    bool injectMouseWheelChange(float delta) override;
    bool injectMousePosition(float x_pos, float y_pos) override;

    bool injectMouseButtonClick(const MouseButton button) override;
    bool injectMouseButtonDoubleClick(const MouseButton button) override;
    bool injectMouseButtonTripleClick(const MouseButton button) override;

    bool injectCopyRequest() override;
    bool injectCutRequest() override;
    bool injectPasteRequest() override;

protected:
    virtual void onMouseButtonClickTimeoutChanged(InputAggregatorEventArgs& args);
    virtual void onMouseButtonMultiClickTimeoutChanged(InputAggregatorEventArgs& args);
    virtual void onMouseButtonMultiClickToleranceChanged(InputAggregatorEventArgs& args);
    virtual void onMouseMoveScalingFactorChanged(InputAggregatorEventArgs& args);

    virtual bool isControlPressed();
    virtual bool isAltPressed();
    virtual bool isShiftPressed();

    void recomputeMultiClickAbsoluteTolerance();
    virtual bool onDisplaySizeChanged(const EventArgs& args);

    Event::Connection d_displaySizeChangedConnection;

    InputEventReceiver* d_inputReceiver;

    //! Timeout used to when detecting a single-click.
    float d_mouseButtonClickTimeout;
    //! Timeout used when detecting multi-click events.
    float d_mouseButtonMultiClickTimeout;
    //! Movement tolerance (percent) used when detecting multi-click events.
    Sizef d_mouseButtonMultiClickTolerance;
    //! Movement tolerance (absolute) used when detecting multi-click events.
    Sizef d_mouseButtonMultiClickAbsoluteTolerance;
    //! should mouse click/multi-click events be automatically generated.
    bool d_generateMouseClickEvents;
    MouseClickTracker* d_mouseClickTrackers;

    //! When set to true will handle semantic actions on key up
    bool d_handleInKeyUp;
    
    //! Scaling factor applied to injected cursor move deltas.
    float d_mouseMovementScalingFactor;

    glm::vec2 d_pointerPosition;
    //! Mapping from a key to its semantic value
    SemanticValue d_keyValuesMappings[UCHAR_MAX]; 
    bool d_keysPressed[UCHAR_MAX];
};

} // End of  CEGUI namespace section

#if defined (_MSC_VER)
#   pragma warning(pop)
#endif

#endif  // end of guard _CEGUIInputEvents_h_
